home *** CD-ROM | disk | FTP | other *** search
/ The Atari Compendium / The Atari Compendium (Toad Computers) (1994).iso / files / prgtools / mint / devices / lpdev.zoo / lpdev.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-08-23  |  9.4 KB  |  384 lines

  1. /*
  2.  * lpdev.c: installs the "/dev/lp" device. It is a
  3.  * buffered Centronics device. This program is free software; see the
  4.  * file "COPYING" for details.
  5.  *
  6.  * This file must be compiled with 16-bit integers.
  7.  *
  8.  * Author:  Thierry Bousch (bousch@suntopo.matups.fr)
  9.  * Version: 0.6 (july 93)
  10.  *
  11.  * Revision history:
  12.  *  0.1: First attempt, using SLEEP instead of NAP: it didn't work.
  13.  *  0.2: Added version number, napping in lp_write(), and the TIOCFLUSH
  14.  *        ioctl function. Cleaned up things a bit.
  15.  *  0.3: Introduced spl7() and spl() to fix competition problems. Added
  16.  *        a few tests before installation, to check that MiNT is running
  17.  *        and the device is not already installed.
  18.  *  0.4: Added file locking. This is completely untested, so be
  19.  *        careful. More cleanup and sanity checks during installation.
  20.  *        Modified the sleep conditions in lp_write and lp_select.
  21.  *  0.5: Deleted the now unnecessary stuff about low and high water marks.
  22.  *        More comments added.
  23.  *  0.6: Added support for the O_NDELAY and O_LOCK flags, inlined spl7/spl.
  24.  *        Moved the definitions to lpdev.h.
  25.  */
  26.  
  27. #include "lpdev.h"
  28. #define  LP_VERSION    "0.6"
  29.  
  30. /*
  31.  * Global variables
  32.  */
  33.  
  34. char *buffer_start, *buffer_end, *buffer_tail;
  35. volatile char *buffer_head;
  36. volatile long buffer_contents;
  37. long selector = 0L;
  38. struct kerinfo *kernel;
  39. struct flock our_lock = { F_WRLCK, 0, 0L, 0L, -1 };
  40.  
  41. /*
  42.  * Forward declarations of the device driver functions
  43.  */
  44.  
  45. long    lp_open        (FILEPTR *f);
  46. long    lp_write    (FILEPTR *f, char *buf, long bytes);
  47. long    lp_read        (FILEPTR *f, char *buf, long bytes);
  48. long    lp_lseek    (FILEPTR *f, long where, int whence);
  49. long    lp_ioctl    (FILEPTR *f, int mode, void *buf);
  50. long    lp_datime    (FILEPTR *f, int *timeptr, int rwflag);
  51. long    lp_close    (FILEPTR *f, int pid);
  52. long    lp_select    (FILEPTR *f, long proc, int mode);
  53. void    lp_unselect    (FILEPTR *f, long proc, int mode);
  54.  
  55. DEVDRV lp_device = {
  56.     lp_open, lp_write, lp_read, lp_lseek, lp_ioctl,
  57.     lp_datime, lp_close, lp_select, lp_unselect
  58. };
  59.  
  60. struct dev_descr devinfo = { &lp_device };
  61.  
  62. /* Initializes the circular buffer */
  63.  
  64. void reset_buffer (void)
  65. {
  66.     int sr = spl7();
  67.     buffer_head = buffer_tail = buffer_start;
  68.     buffer_end = buffer_start + BUFSIZE;
  69.     buffer_contents = 0L;
  70.     spl(sr);
  71. }
  72.  
  73. /* Copyright information */
  74.  
  75. void Version (void)
  76. {
  77.     Cconws("Spooled Centronics device driver, by T.Bousch (version "
  78.     LP_VERSION ").\r\n"
  79.     "This program is FREE SOFTWARE, and comes with NO WARRANTY.\r\n"
  80.     "See the file \"COPYING\" for more information.\r\n");
  81. }
  82.  
  83. /* 
  84.  * Installs everything, returns 0 on success. Must be executed in
  85.  * supervisor mode.
  86.  */
  87.  
  88. long install_things (void)
  89. {
  90.     if (Syield() == EINVFN) {
  91.         Cconws("lpdev: MiNT is not running\r\n");
  92.         return EACCDN;
  93.     }
  94.     if (Fsfirst(DEVNAME, 0) == 0) {
  95.         Cconws("lpdev: device \"" DEVNAME "\" already installed\r\n");
  96.         return EACCDN;
  97.     }
  98.     if (*(char*)0xFFFFFA09 &    /* IERB */
  99.         *(char*)0xFFFFFA15 & 1) {    /* IMRB */
  100.         Cconws("lpdev: Centronics interrupt already in use\r\n");
  101.         return EACCDN;
  102.     }
  103.     buffer_start = (char *)Malloc(BUFSIZE);
  104.     if (!buffer_start) {
  105.         Cconws("lpdev: not enough memory\r\n");
  106.         return ENSMEM;
  107.     }
  108.     reset_buffer();
  109.  
  110.     kernel = (struct kerinfo *)Dcntl(DEV_INSTALL, DEVNAME, &devinfo);
  111.     if ((long)kernel <= 0L) {
  112.         Cconws("lpdev: unable to install device\r\n");
  113.         return EACCDN;
  114.     }
  115.     /* Finally! */
  116.     Mfpint(0, new_centr_vector);
  117.     Jenabint(0);
  118.  
  119.     return 0;
  120. }
  121.  
  122. /*
  123.  * The main routine is very simple now, it just calls install_things
  124.  * and remains resident if everything went well
  125.  */
  126.  
  127. int main()
  128. {
  129.     long ret;
  130.     
  131.     ret = Supexec(install_things);
  132.     if (ret < 0)
  133.         return ret;
  134.  
  135.     /* Installation is complete */
  136.     Version();
  137.     Ptermres(256L + _base->p_tlen + _base->p_dlen + _base->p_blen, 0);
  138.     return -999;    /* never reached, just to make Gcc happy */
  139. }
  140.  
  141. /*
  142.  * Will wake any process select'ing the printer;
  143.  * this routine is called by the interrupt handler, but also when the
  144.  * buffer is flushed.
  145.  */
  146.  
  147. void wake_up (void)
  148. {
  149.     if (selector)
  150.         WAKESELECT(selector);    /* wake selector */
  151. }
  152.  
  153. /*
  154.  * Sends as many bytes as possible (usually one) to the printer until
  155.  * he gets busy. This routine is called by lp_write and by the
  156.  * interrupt handler, so it _must_ be multi-thread. It will not work if
  157.  * you remove the spl7()/spl() pair.
  158.  *
  159.  * On a more general note, it is safest to disable all interrupts before
  160.  * modifying the volatile variables (buffer_contents and buffer_head).
  161.  */
  162.  
  163. #define PRINTER_BUSY    (*(char*)0xFFFFFA01 & 1)
  164.  
  165. void print_head (void)
  166. {
  167.     int sr = spl7();
  168.     while (!PRINTER_BUSY && buffer_contents) {
  169.         print_byte( *buffer_head );
  170.         --buffer_contents;
  171.         if (++buffer_head >= buffer_end)
  172.             buffer_head -= BUFSIZE;
  173.     }
  174.     spl(sr);
  175. }
  176.  
  177. /*
  178.  * Copies a linear buffer into the circular one. We assume that's there
  179.  * enough room for this operation, ie
  180.  * nbytes + buffer_contents <= BUFSIZE
  181.  *
  182.  * Note: the while() loop will be executed at most twice.
  183.  * Note2: the instruction "buffer_contents += N" looks atomic, but it
  184.  *   isn't (the Gcc outputs several assembly instructions). Therefore it
  185.  *   must be wrapped in spl7()/spl().
  186.  */
  187.  
  188. void print_tail (char *buf, long nbytes)
  189. {
  190.     long N;
  191.     int sr;
  192.     
  193.     while (nbytes) {
  194.         N = buffer_end - buffer_tail;
  195.         if (N > nbytes)
  196.             N = nbytes;
  197.         bcopy (buf, buffer_tail, N);
  198.         buf += N; nbytes -= N;
  199.         sr = spl7();
  200.         buffer_contents += N;
  201.         spl(sr);
  202.         buffer_tail += N;
  203.         if (buffer_tail >= buffer_end)
  204.             buffer_tail -= BUFSIZE;
  205.     }
  206.     print_head();    /* To initiate printing */
  207. }
  208.  
  209. /*
  210.  * Here are the actual device driver functions
  211.  */
  212.  
  213. #define  LP_LOCKED    (our_lock.l_pid >= 0)
  214.  
  215. long lp_open (FILEPTR *f)
  216. {
  217.     TRACE(("lp: open device"));
  218.     return 0;
  219. }
  220.  
  221. long lp_close (FILEPTR *f, int pid)
  222. {
  223.     TRACE(("lp: close device"));
  224.     if ((f->flags & O_LOCK) && our_lock.l_pid == pid) {
  225.         TRACE(("lp: releasing lock on close"));
  226.         f->flags &= ~O_LOCK;
  227.         our_lock.l_pid = -1;
  228.         WAKE(IO_Q, (long)&our_lock);
  229.     }
  230.     return 0;
  231. }
  232.  
  233. long lp_read (FILEPTR *f, char *buf, long bytes)
  234. {
  235.     TRACE(("lp: foolish attempt to read"));
  236.     return 0;
  237. }
  238.  
  239. long lp_datime (FILEPTR *f, int *timeptr, int rwflag)
  240. {
  241.     if (rwflag) {
  242.         DEBUG(("lp: can't modify date/time"));
  243.         return EACCDN;
  244.     }
  245.     TRACE(("lp: read time and date"));
  246.     *timeptr++ = TGETTIME();
  247.     *timeptr   = TGETDATE();
  248.     return 0;
  249. }
  250.  
  251. long lp_lseek (FILEPTR *f, long where, int whence)
  252. {
  253.     TRACE(("lp: foolish attempt to seek"));
  254.     if (whence < 0 || whence > 2)
  255.         return EINVFN;
  256.     return where ? ERANGE : 0L;
  257. }
  258.  
  259. long lp_ioctl (FILEPTR *f, int mode, void *buf)
  260. {
  261.     struct flock *g;
  262.  
  263.     if (mode == FIONREAD) {
  264.         TRACE(("lp: ioctl(FIONREAD)"));
  265.         *(long *)buf = 0L;
  266.     }
  267.     else if (mode == FIONWRITE) {
  268.         TRACE(("lp: ioctl(FIONWRITE)"));
  269.         *(long *)buf = BUFSIZE - buffer_contents;
  270.     }
  271.     else if (mode == TIOCFLUSH) {
  272.         TRACE(("lp: clear buffer"));
  273.         reset_buffer();
  274.         wake_up();    /* Wake up any select'ing process */
  275.     }
  276.     else if (mode == F_GETLK) {
  277.         g = (struct flock *) buf;
  278.  
  279.         if (LP_LOCKED) {
  280.             TRACE(("lp: get_lock succeeded"));
  281.             *g = our_lock;
  282.         } else {
  283.             TRACE(("lp: get_lock failed"));
  284.             g->l_type = F_UNLCK;
  285.         }
  286.     }
  287.     else if (mode == F_SETLK || mode == F_SETLKW) {
  288.         g = (struct flock *) buf;
  289.  
  290.         switch (g->l_type) {
  291.         case F_UNLCK:
  292.             if (!(f->flags & O_LOCK) || g->l_pid != our_lock.l_pid) {
  293.             DEBUG(("lp: no such lock"));
  294.             return ENSLOCK;
  295.             } else {
  296.             TRACE(("lp: remove lock"));
  297.             f->flags &= ~O_LOCK;
  298.             our_lock.l_pid = -1;
  299.             WAKE(IO_Q, (long)&our_lock);
  300.             }
  301.             return 0;
  302.         case F_RDLCK:
  303.             TRACE(("lp: read locks are ignored"));
  304.             return 0;
  305.         case F_WRLCK:
  306.             while (LP_LOCKED) {
  307.             DEBUG(("lp: conflicting locks"));
  308.             if (mode == F_SETLK) {
  309.                 *g = our_lock;
  310.                 return ELOCKED;
  311.             }
  312.             SLEEP(IO_Q, (long)&our_lock);
  313.             }
  314.             TRACE(("lp: set lock"));
  315.             f->flags |= O_LOCK;
  316.             our_lock.l_pid = g->l_pid;
  317.             return 0;
  318.         default:
  319.             DEBUG(("lp: invalid lock type"));
  320.             return EINVFN;
  321.         }
  322.     }
  323.     else {
  324.         DEBUG(("lp: invalid ioctl mode"));
  325.         return EINVFN;
  326.     }
  327.     return 0;
  328. }
  329.  
  330. long lp_write (FILEPTR *f, char *buf, long bytes)
  331. {
  332.     long _bytes = bytes;
  333.     long N;
  334.     int  ndel = (f->flags & O_NDELAY);    /* don't wait */
  335.  
  336.     while (bytes) {
  337.         N = BUFSIZE - buffer_contents;
  338.         /*
  339.          * If the data won't fit into the buffer,
  340.          * and if the buffer itself is almost full, we won't
  341.          * be able to copy much. So we better sleep a bit (unless
  342.          * the O_NDELAY flag is set).
  343.          */
  344.         if (N < bytes && N < BUFSIZE/4 && !ndel) {
  345.             TRACE(("lp: napping in lp_write"));
  346.             NAP(200);    /* let's wait 200 milliseconds */
  347.             continue;    /* and try again */
  348.         }
  349.         if (bytes < N)
  350.             N = bytes;
  351.         /* Now N contains the number of bytes we want to copy
  352.          * into the circular buffer */
  353.         print_tail(buf, N);
  354.         buf += N;
  355.         bytes -= N;
  356.         /*
  357.          * If the O_NDELAY flag is set, we don't make a second
  358.          * attempt to write the remaining "bytes" bytes.
  359.          */
  360.         if (ndel)  break;
  361.     }
  362.     TRACE(("lp: wrote %ld bytes, skipped %ld", _bytes-bytes, bytes));
  363.     return _bytes - bytes;
  364. }
  365.  
  366. /* Bug: only one process can select the printer */
  367.  
  368. long lp_select (FILEPTR *f, long proc, int mode)
  369. {
  370.     if (buffer_contents == BUFSIZE && !selector) {
  371.         TRACE(("lp: select returned 0"));
  372.         selector = proc;
  373.         return 0;
  374.     }
  375.     TRACE(("lp: select returned 1"));
  376.     return 1;
  377. }
  378.  
  379. void lp_unselect (FILEPTR *f, long proc, int mode)
  380. {
  381.     TRACE(("lp: unselect"));
  382.     selector = 0L;
  383. }
  384.